package dados;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;

public class Instancia {

	private Integer qntDados;

	private Integer qntTorres;
	
	private HashMap<Integer, Torre> torresLista;

	private Solucao solucaoAtual;

	private Solucao melhorSolucao;

	private BigDecimal custoSolucao;

	private BigDecimal mediaCusto;

	public Instancia() {
		// construtor vazio
	}

	public Instancia(Integer qntDados, Integer qntTorres, int mediaCusto, HashMap<Integer, Torre> hashMap) {
		this.qntDados = qntDados;
		this.qntTorres = qntTorres;
		this.mediaCusto = new BigDecimal(mediaCusto);
		this.setTorresLista(hashMap);
	}

	public Integer getQntDados() {
		return qntDados;
	}

	public void setQntDados(Integer qntDados) {
		this.qntDados = qntDados;
	}

	public Integer getQntTorres() {
		return qntTorres;
	}

	public void setQntTorres(Integer qntTorres) {
		this.qntTorres = qntTorres;
	}

	public void setCustoSolucao(BigDecimal custoSolucao) {
		this.custoSolucao = custoSolucao;
	}

	public BigDecimal getCustoSolucao() {
		return custoSolucao;
	}

	public Solucao getSolucaoAtual() {
		return solucaoAtual;
	}

	public void setSolucaoAtual(Solucao solucaoAtual) {
		this.solucaoAtual = solucaoAtual;
	}

	public void setMelhorSolucao(Solucao melhorSolucao) {
		this.melhorSolucao = melhorSolucao;
	}

	public Solucao getMelhorSolucao() {
		return melhorSolucao;
	}

	public void setMediaCusto(BigDecimal mediaCusto) {
		this.mediaCusto = mediaCusto;
	}

	public BigDecimal getMediaCusto() {
		return mediaCusto;
	}

	/**
	 * @param torresLista the torresLista to set
	 */
	public void setTorresLista(HashMap<Integer, Torre> torresLista) {
		this.torresLista = torresLista;
	}

	/**
	 * @return the torresLista
	 */
	public HashMap<Integer, Torre> getTorresLista() {
		return torresLista;
	}

	/**
	 * Gera a lista de vizinhos da solucao s
	 * 
	 * @param vizinhosLista
	 *            TODO
	 * @param s
	 *            : a solucao para a qual pretende-se estabelecer os vizinhos
	 * @return Lista de vizinhos de s
	 */
	public void calculaVizinhos(Solucao s, ArrayList<Solucao> vizinhosLista) {

		Solucao vizinhoTabu = new Solucao();
		vizinhosLista.add(new Solucao());

		for (Torre t : torresLista.values()) {
			if (s.getSolucao().contains(t.getIdInteger())) {
				// remove torre e seta tabu
				Solucao vizinho = new Solucao(this, s);
				vizinho.removeTorre(this, t.getIdInteger());
				vizinho.setTorreInvertida(t);
				adicionaVizinho(vizinhosLista, vizinhoTabu, vizinho);

				// gera novos vizinhos com torres que nao estao na solucao
//				for (Torre tlinha : torresLista.values()) {
//					if (!s.getSolucao().contains(tlinha.getIdInteger())) {
//						Solucao vlinha = new Solucao(this, vizinho);
//						vlinha.addTorre(this, tlinha.getIdInteger());
//						vlinha.setTorreInvertida(tlinha);
//						adicionaVizinho(vizinhosLista, vizinhoTabu, vlinha);
//					}
//				}

			} else {
				Solucao vizinho = new Solucao(this, s);
				vizinho.addTorre(this, t.getIdInteger());
				vizinho.setTorreInvertida(t);
				adicionaVizinho(vizinhosLista, vizinhoTabu, vizinho);
			}
		}
	}

	private void adicionaVizinho(ArrayList<Solucao> vizinhosLista,
			Solucao vizinhoTabu, Solucao vizinho) {
		// caso seja tabu e valida, adiciona na lista de vizinhos
		if (vizinho.getTorreInvertida().getTabu() != 0
				&& vizinho.getQtdPontos() >= qntDados) {
			// criterio de aspiracao
			if (vizinho.getCusto().compareTo(melhorSolucao.getCusto()) < 0) {
				vizinhosLista.add(vizinho);
				melhorVizinho(vizinhosLista, this);
			}
		} else {
			vizinhosLista.add(vizinho);
			melhorVizinho(vizinhosLista, this);
		}
	}

	private void melhorVizinho(ArrayList<Solucao> vizinhosLista,
			Instancia instancia) {
		Solucao melhorVizinho = new Solucao();

		if (vizinhosLista.get(0).getCusto() == BigDecimal.ZERO
				|| funcaoObjetivo(vizinhosLista.get(0), instancia).compareTo(
						funcaoObjetivo(vizinhosLista.get(1), instancia)) > 0) {
			melhorVizinho = vizinhosLista.get(1);
		} else {
			melhorVizinho = vizinhosLista.get(0);
		}
		// return melhorVizinho;
		vizinhosLista.clear();
		vizinhosLista.add(melhorVizinho);
	}

	private BigDecimal funcaoObjetivo(Solucao vizinho, Instancia instancia) {

		BigDecimal diferenca = new BigDecimal(vizinho.getQtdPontos()
				- instancia.getQntDados());

		// calcula funcao objetivo
		BigDecimal objetivo = vizinho.getCusto().subtract(
				instancia.getMediaCusto().multiply(
						BigDecimal.ZERO.min(diferenca)));

		return objetivo;
	}
}
